Skip to content

feat: scheduled supervision sweeps + manual command (PRD #17)#21

Merged
Catafal merged 4 commits into
mainfrom
feat/supervise-sweeps
Jun 13, 2026
Merged

feat: scheduled supervision sweeps + manual command (PRD #17)#21
Catafal merged 4 commits into
mainfrom
feat/supervise-sweeps

Conversation

@Catafal

@Catafal Catafal commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Implements PRD #17 — a cheap, scheduled alternative to the always-on observer daemon, plus a manual trigger. Addresses the long-term cost of continuous supervision: the daemon's ~5-min judge tick is ~180 codex gpt-5.4-mini calls per session per day; 5 sweeps/day ≈ 36× cheaper.

What shipped (issues #18#20)

  • Sweep service + bach supervise (manual one-shot supervision) #18 sweep service + bach supervise — one-shot supervision sweep, no persistent process: enumerate live task-linked sessions → read each transcript tail → judge once → apply the verdict → per-session summary → exit. Manual via bare bach supervise / run, with --task <id> and --force. Extracted the verdict-apply path into verdict_apply.py so the sweep and the watcher share one observer-authority apply path. Per-session error isolation; active-hours window guard. New window config knobs (start=8, end=22).
  • launchd calendar agent: bach supervise install/uninstall (5x/day daytime) #19 launchd calendar agentbach supervise install / uninstall registers com.bach.supervise with a StartCalendarInterval array of N daytime entries (default 5, spread across 08:00–22:00). Idempotent; printed bach supervise fallback on launchctl failure; distinct label from the daemon. New supervise_sweeps_per_day=5 knob.
  • docs: ADR-018 sweep-vs-daemon + supervise how-to + CLAUDE.md #20 docs — ADR-018 (sweep-vs-daemon trade-off + keep-both decision), how-to, CLAUDE.md commands, intent comments.

Cost model

bach supervise does exactly one judge call per session per run. Run 5×/day during daylight (08:00–22:00) → flat, predictable cost. The schedule spreads to hours [8, 11, 14, 16, 19] for N=5 (review fixed an initial floor-division bug that clustered them at [8,10,12,14,16], leaving the late afternoon uncovered).

Relationship to the daemon (PRD #10)

The always-on daemon is untouched and remains the opt-in real-time option. Scheduled sweeps are the recommended cost-conscious default. Both may be installed (distinct labels) but that's redundant — documented in ADR-018. Supervision stays pull, not push (verdicts land on the artifact/board).

Quality

  • Built TDD across 4 sonnet agents (2 implementation + review + docs), make gate verified between slices.
  • Review (/review + /backend-taste + /qa, no HITL) fixed the sweep-hour spread + added the spread-assertion test that let the bug hide; verdict-apply extraction, window guard, error isolation, idempotent install, and fallback all reviewed "ship it".
  • make gate green: 1225 passed, 1 skipped (ruff + mypy + pytest).

Closes #17, #18, #19, #20

🤖 Generated with Claude Code

Catafal and others added 4 commits June 13, 2026 13:15
Extract the verdict-apply path into services/verdict_apply.py so the
watcher and the new sweep share ONE observer-authority apply path.
run_sweep (deep, injected seams) enumerates live linked sessions, reads
each transcript tail, judges once via judge_session, applies the verdict,
and returns a per-session SweepReport — with an active-hours window guard
(--force bypass) and per-session error isolation so one bad session never
aborts the sweep. bach supervise runs it manually (bare invocation or
'run'; --task, --force). New window config knobs (start=8, end=22,
validated 0<=start<end<=24).

Closes #18

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
render_sweep_agent emits a com.bach.supervise plist with a
StartCalendarInterval array of N daytime entries invoking bach supervise.
install/uninstall_sweep_agent are idempotent (unload-before-overwrite)
via an injected launchctl runner, printing the manual 'bach supervise'
fallback on launchctl failure. New supervise_sweeps_per_day=5 knob.
Distinct label from the daemon so both can coexist.

Closes #19

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…(PRD #17)

_compute_sweep_hours used floor division (step=span//N), clustering N=5
over [8,22) into [8,10,12,14,16] and leaving 16:00-22:00 unsupervised.
Switch to round(start + i*span/N) → [8,11,14,16,19], spanning the window.
Add the N=5 spread assertion that previously let the clustering hide
(only count + in-window were checked). /review + /backend-taste + /qa
otherwise ship-it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ADR-018 records the scheduled-sweep vs always-on-daemon trade-off (~36x
cheaper), the keep-both decision, and accepted limitations. New how-to
covers bach supervise (manual/--force), supervise install (5x/day daytime,
[8,11,14,16,19]), tuning knobs, and when to use sweeps vs the daemon.
CLAUDE.md gains the supervise command block. Surgical /code-comments pass
adds WHY intent across the sweep modules; corrected the spacing example
to the accurate floored-step [8,10,12,14,16] vs round [8,11,14,16,19].

Closes #20

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Catafal Catafal merged commit b373b05 into main Jun 13, 2026
2 checks passed
@Catafal Catafal deleted the feat/supervise-sweeps branch June 13, 2026 11:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PRD: Scheduled supervision sweeps + manual command (cheap mode)

1 participant